# 服务指纹识别源码
import socket
import threading
import json
import ipaddress

# dnspythony模块引入
from dns import resolver
# geoip引入
import geoip2.database
# python-nmap引入
import nmap

from PortScan import Scan
from DomainMining import domainToip

# 引入Scapy
from scapy.layers.inet import TCP, IP
from scapy.sendrecv import sr1, send

# geoip数据库路径：
geodb = "./GeoLite2-ASN.mmdb"
# 指纹文件路径
portfingerfile = "./fingerprint/port-service.json"
cdnfingerfile = "./fingerprint/cdn.json"
# SqlMap-waf指纹库：
wafingerfile = "./fingerprint/waf.json"

def initFingerprint(fingermode = 1):
    try:
        # 模式1：打开端口指纹文件
        if fingermode == 1:
            return open(portfingerfile,"r",encoding="utf8")
        # 模式2：打开CDN指纹文件
        if fingermode == 2:
            return open(cdnfingerfile,"r",encoding="utf8")
        # 模式3：打开waf指纹库文件
        if fingermode == 3:
            return open(wafingerfile,"r",encoding="utf8")
    except Exception as e:
        print(e)
        return None

def initGeoip():
    try:
        return geoip2.database.Reader(geodb)
    except Exception as e:
        print(e)
        return None

# 通过端口识别服务：
def PortAnalyze(ip):
    results = []
    try:
        portjson = initFingerprint(1)
        portdict = json.load(portjson)
        ports = Scan(ip,0,65535)
        for result in ports:
            if str(result) in portdict:
               dict = {
                   "port":result,
                   "service":portdict[str(result)]
               }
               results.append(dict)
    except Exception as e:
        print(e)
    return results


# 通过网络特征识别CDN：
# 通过ip所在子网判断：
def IpIsCDN(ip,cdn_list):
    try:
        for network in cdn_list:
            if ipaddress.ip_address(ip) in ipaddress.ip_network(network):
                return True
    except Exception as e:
        print(e)
    return False
# 通过CNAME判断：
def CnameIsCDN(domain,cdn_list):
    try:
        results = resolver.resolve(domain,"CNAME")
        for result in results.response.answer:
            for cdn in cdn_list:
                if cdn in result.to_text():
                    return True
    except Exception as e:
        print(e)
    return False
# 通过ASN判断：
def ASNIsCDN(ip,cdn_list):
    geo = initGeoip()
    try:
        result = geo.asn(ip)
        if str(result.autonomous_system_number) in cdn_list:
            return True
    except Exception as e:
        print(e)
    return False
# CDN识别主函数
def CheckCDN(domain):
    cdnjson = initFingerprint(2)
    cdnfinger = json.load(cdnjson)
    if CnameIsCDN(domain,cdnfinger["all_CNAME"]):
        return True
    iplist = domainToip(domain)
    for ip in iplist:
        if IpIsCDN(ip,cdnfinger["cdns_ip"]):
            return True
        elif ASNIsCDN(ip,cdnfinger["ASNS"]):
            return True
    return False

# 通过数据包特征简单识别OS：
def checkOS(ip,port):
    result = {
        "OS":None
    }
    try:
        # 发送一个握手包
        ans = sr1(IP(dst=ip) /
                  TCP(dport=port, flags="S"),
                  timeout=1, verbose=False)
        # 回显判断
        if ans.haslayer(IP):
            ttl = ans.getlayer(IP).ttl
            if ttl <= 64:
                result["OS"] = "Linux/Unix"
            elif ttl <= 128:
                result["OS"] = "Windows"
            elif ttl <= 255:
                result["OS"] = "Unix"
            # 发送挥手包
            send(IP(dst=ip) /
                TCP(dport=port, flags='R'),
                verbose=False)
    except Exception as e:
        print(e)
    return result
# 利用Nmap识别OS：
def checkOS_nmap(ip,port):
    # 创建扫描对象
    scanner = nmap.PortScanner()
    result = {
        "OS":None
    }
    try:
        # 开始扫描
        response = scanner.scan(hosts=ip,ports=str(port),arguments="-O")
        if response["scan"][ip]["osmatch"] != None:
            result["OS"] = response["scan"][ip]["osmatch"][0]
    except Exception as e:
        print(e)
    return result



# 通过文件特征识别Waf：


# 通过标语特征识别设备：


if __name__ == "__main__":
    ip = ""
    domain = ""
    results = PortAnalyze(ip)
    print(results)
    if CheckCDN(domain):
        print(domain + " has CDN")
    print("TTL识别结果：")
    print(checkOS(ip,80))
    print("Nmap识别结果：")
    print(checkOS_nmap(ip,80))
